<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Add a 3D model</title>
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <script src="../../lib/mapbox-gl.js"></script>
    <link href="../../lib/mapbox-gl.css" rel="stylesheet" />
    <script src="../../lib/axios.min.js"></script>
    <script src="../../lib/three.min.js"></script>
    <script src="../../lib/GLTFLoader.js"></script>
    <style>
      body {
        margin: 0;
        padding: 0;
      }
      #map {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script>
      let baseDataUrl = `http://${localStorage.getItem(
        "host"
      )}/mapbox-gl-js-offline-examples`;
      axios
        .get(
          baseDataUrl +
            `/resources/styles/${localStorage.getItem(
              "mbtilesStylesName"
            )}.json`
        )
        .then((res) => {
          let data = res.data;
          initMap(data);
        })
        .catch((err) => {
          console.error(err);
        });

      function initMap(style) {
        let map = new mapboxgl.Map({
          container: "map",
          zoom: 6,
          pitch: 60,
          center: [103.463, 33.415],
          style: style,
        });

        // parameters to ensure the model is georeferenced correctly on the map
        var modelOrigin = [103.463, 33.415];
        var modelAltitude = 0;
        var modelRotate = [Math.PI / 2, 0, 0];
        var modelScale = 15500.41843220338983e-8;

        // transformation parameters to position, rotate and scale the 3D model onto the map
        var modelTransform = {
          translateX: mapboxgl.MercatorCoordinate.fromLngLat(
            modelOrigin,
            modelAltitude
          ).x,
          translateY: mapboxgl.MercatorCoordinate.fromLngLat(
            modelOrigin,
            modelAltitude
          ).y,
          translateZ: mapboxgl.MercatorCoordinate.fromLngLat(
            modelOrigin,
            modelAltitude
          ).z,
          rotateX: modelRotate[0],
          rotateY: modelRotate[1],
          rotateZ: modelRotate[2],
          scale: modelScale,
        };

        var THREE = window.THREE;

        // configuration of the custom layer for a 3D model per the CustomLayerInterface
        var customLayer = {
          id: "3d-model",
          type: "custom",
          renderingMode: "3d",
          onAdd: function (map, gl) {
            this.camera = new THREE.Camera();
            this.scene = new THREE.Scene();

            // create two three.js lights to illuminate the model
            var directionalLight = new THREE.DirectionalLight(0xffffff);
            directionalLight.position.set(0, -70, 100).normalize();
            this.scene.add(directionalLight);

            var directionalLight2 = new THREE.DirectionalLight(0xffffff);
            directionalLight2.position.set(0, 70, 100).normalize();
            this.scene.add(directionalLight2);

            // use the three.js GLTF loader to add the 3D model to the three.js scene
            var loader = new THREE.GLTFLoader();
            loader.load(
              baseDataUrl + "/data/model/34M_17/34M_17.gltf",
              function (gltf) {
                this.scene.add(gltf.scene);
              }.bind(this)
            );
            this.map = map;

            // use the Mapbox GL JS map canvas for three.js
            this.renderer = new THREE.WebGLRenderer({
              canvas: map.getCanvas(),
              context: gl,
            });

            this.renderer.autoClear = false;
          },
          render: function (gl, matrix) {
            var rotationX = new THREE.Matrix4().makeRotationAxis(
              new THREE.Vector3(1, 0, 0),
              modelTransform.rotateX
            );
            var rotationY = new THREE.Matrix4().makeRotationAxis(
              new THREE.Vector3(0, 1, 0),
              modelTransform.rotateY
            );
            var rotationZ = new THREE.Matrix4().makeRotationAxis(
              new THREE.Vector3(0, 0, 1),
              modelTransform.rotateZ
            );

            var m = new THREE.Matrix4().fromArray(matrix);
            var l = new THREE.Matrix4()
              .makeTranslation(
                modelTransform.translateX,
                modelTransform.translateY,
                modelTransform.translateZ
              )
              .scale(
                new THREE.Vector3(
                  modelTransform.scale,
                  -modelTransform.scale,
                  modelTransform.scale
                )
              )
              .multiply(rotationX)
              .multiply(rotationY)
              .multiply(rotationZ);

            this.camera.projectionMatrix.elements = matrix;
            this.camera.projectionMatrix = m.multiply(l);
            this.renderer.state.reset();
            this.renderer.render(this.scene, this.camera);
            this.map.triggerRepaint();
          },
        };
        map.on("style.load", function () {
          map.addLayer(customLayer);
        });
      }
    </script>
  </body>
</html>
